/**
 * 
 */
package co.fitstart.entity.course.schedule;

import java.beans.Transient;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.annotations.DynamicUpdate;
import org.springframework.format.annotation.DateTimeFormat;
import com.demo2do.core.format.PersistentFormat;
import com.demo2do.core.utils.DateFormatUtils;
import com.demo2do.core.utils.DateUtils;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonIdentityReference;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import co.fitstart.entity.course.Course;
import co.fitstart.entity.course.CourseType;
import co.fitstart.entity.course.round.CourseRound;
import co.fitstart.entity.courseware.exercise.ExerciseContent;
import co.fitstart.entity.principal.Principal;
import co.fitstart.entity.site.RoomScheduleRecord;
import co.fitstart.entity.site.RoomType;
import co.fitstart.entity.site.Site;
import co.fitstart.entity.user.Student;

/**
 * @author downpour
 *
 */
@Entity
@Table(name = "course_schedule")
@DynamicUpdate
@JsonIgnoreProperties(value={"hibernate_lazy_initializer","handler","fieldHandler"})
public class CourseSchedule {
    
    @Id
    @GeneratedValue
    private Long id;
    
    @Column(name = "schedule_sequence")
    private int sequence;
    
    /** 对应学员  */
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
    @JsonIdentityReference(alwaysAsId=true)
    private Student student;
    
    /** 对应教练 */
    @ManyToOne(fetch = FetchType.LAZY, optional = true)
    @PersistentFormat
    @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
    @JsonIdentityReference(alwaysAsId=true)
    private Principal coach;
    
    /** 对应课程 */
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
    @JsonIdentityReference(alwaysAsId=true)
    private Course course;
    
    /** 所属门店 */
    @ManyToOne(fetch = FetchType.LAZY, optional = true)
    @PersistentFormat
    @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
    @JsonIdentityReference(alwaysAsId=true)
    private Site site;
    
    /** 
     * 对应课次
     * 
     * 团课为后绑定  / 非团课为前绑定
     * 
     */
    @ManyToOne(fetch = FetchType.LAZY, optional = true)
    @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
    @JsonIdentityReference(alwaysAsId=true)
    private CourseRound courseRound;
    
    /** 课程类型 (便于后台列表功能-------类型+课程状态能区分大部分课程列表) */
    @Enumerated(EnumType.ORDINAL)
    private CourseType courseType;
    
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @org.hibernate.annotations.Type(type = "date")
    private Date occurDate;
    
    private String occurHour;
    
    /** 房间记录表 */
    @ManyToOne(fetch = FetchType.LAZY, optional = true)
    @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
    @JsonIdentityReference(alwaysAsId=true)
    private RoomScheduleRecord roomSchedule;
    
    /** 房间类型 */
    @Enumerated(EnumType.ORDINAL)
    private RoomType roomType;
    
    /** 状态 */
    @Enumerated(EnumType.ORDINAL)
    private ScheduleStatus scheduleStatus;

    private String comment;

    /** 评分（教练填写） */
    private Integer grade;

    /** 上课内容 */
    private String content;
    
    private Date signinTime;
    
    @Column(name = "is_disabled")
    private boolean disabled;
    
    /**
     * The default constructor
     */
    public CourseSchedule() {
        
    }
    
    /**
     * 
     * @param courseRound
     * @param courseSchedule
     */
    public CourseSchedule(CourseRound courseRound, CourseSchedule courseSchedule) {
        this.courseRound = courseRound;
        this.student = courseRound.getStudent();
        this.site = courseSchedule.getSite();
        
        this.coach = courseSchedule.getCoach();
        this.course = courseSchedule.getCourse();
        this.courseType = courseSchedule.getCourseType();
        this.occurDate = courseSchedule.getOccurDate();
        this.occurHour = courseSchedule.getOccurHour();
        this.scheduleStatus = courseSchedule.getScheduleStatus();
        this.comment = courseSchedule.getComment();
        this.disabled = courseSchedule.isDisabled();
    }
    
    /**
     * 
     * @param courseRound
     * @param scheduleStatus
     */
    public CourseSchedule(CourseRound courseRound, ScheduleStatus scheduleStatus) {
        
        this.courseRound = courseRound;
        this.student = courseRound.getStudent();
        this.course = courseRound.getCourse();
        this.site = courseRound.getSite();
        
        this.courseType = courseRound.getCourseType();
        this.scheduleStatus = scheduleStatus;
        
        this.disabled = false;
    }
    
    /**
     * 
     * @param courseRound
     * @param site
     * @param coach
     * @param day
     * @param hour
     */
    public CourseSchedule(CourseRound courseRound, Site site, Principal coach, Date day, String hour) {
        this.courseRound = courseRound;
        this.student = courseRound.getStudent();
        this.course = courseRound.getCourse();
        this.coach = coach;
        this.site = site;
        this.occurDate = day;
        this.occurHour = hour;
        
        this.courseType = courseRound.getCourseType();
        this.scheduleStatus = ScheduleStatus.READY;
        
        this.disabled = false;
    }
    
    /**
     * 
     * @param roomScheduleRecord
     * @return
     */
    public CourseSchedule roomScheduleRecord(RoomScheduleRecord roomScheduleRecord) {
        this.roomSchedule = roomScheduleRecord;
        this.roomType = roomScheduleRecord.getType();
        return this;
    }
    
    /**
     * 
     * @param student
     * @return
     */
    public boolean matches(Student student) {
        return this.student.getId().equals(student.getId());
    }
    
    /**
     * 
     * @param comment
     * @return
     */
    public CourseSchedule signin(String comment) {
        
        // add comment
        this.comment = comment;
        
        this.signinTime = new Date();
        
        // change status
        this.scheduleStatus = ScheduleStatus.FINISH;
        
        return this;
    }
    
    /**
     * 
     * @return
     */
    @Transient
    public String occurTime() {
      String dateTime = DateFormatUtils.format(this.occurDate, "yyyy-MM-dd") + " " + this.occurHour + ":00";
      return dateTime;
    }

    /**
     * 
     * @return
     */
    @Transient
    public boolean couldSignin() {
        
        String dateTime = DateFormatUtils.format(this.occurDate, "yyyy-MM-dd") + " " + this.occurHour + ":00";
        Date occurDateTime = DateUtils.parseDate(dateTime, "yyyy-MM-dd HH:mm:SS");
        
        long between = new Date().getTime() - occurDateTime.getTime();
        
        double hours = (double)between / (60 * 60 * 1000); 
        
        if(hours < -1 || hours > 2) {
            return false;
        }
        
        return true;
    }
    
    /**
     * 
     * @return
     */
    @Transient
    public boolean expiry() {
        String dateTime = DateFormatUtils.format(this.occurDate, "yyyy-MM-dd") + " " + this.occurHour + ":00";
        Date occurDateTime = DateUtils.parseDate(dateTime, "yyyy-MM-dd HH:mm:ss");
        
        if(new Date().getTime() - occurDateTime.getTime() > 2*60*60*1000) {
            return true;
        } else {
            return false;
        }
    }
    
    /**
     * 
     * @return
     */
    @Transient
    public boolean couldEdit() {
        // check the course round's status  
        if(this.courseRound.getCourseStatus().isActivate() ||this.courseRound.getCourseStatus().isCancel()
                || this.courseRound.getCourseStatus().isExpire() || this.courseRound.getCourseStatus().isFinish()
                || this.courseRound.getCourseStatus().isHold()) {
            return false;
        }
        
        // check schedule's status
        if(!this.scheduleStatus.isReady()) {
            return false;
        }
        
        //1. Only individual courseSchedule can book 
        CourseType courseType = this.courseType;
        if(courseType == null || !courseType.isIndividual() || !courseType.isPosture() || !courseType.isSpecial()) {
            return false;
        }
        
        //2. Get date times between (ms)
        Date today = new Date();
        Date occurDateTime = this.generateDate();
        long between = occurDateTime.getTime() - today.getTime();
        
        //3. decide could book or not
        if(this.course.isExperience()) {
            long days = between / (24 * 60 * 60 * 1000);
            return days < 1 ? false : true;
        }else {
        	return between > 0 ? true : false;
        }
    }
    
    /**
     * 
     * @return
     */
    @Transient
    public boolean couldCancel() {
        // check the course round's status  
        if(this.courseRound.getCourseStatus().isActivate() ||this.courseRound.getCourseStatus().isCancel()
                || this.courseRound.getCourseStatus().isExpire() || this.courseRound.getCourseStatus().isFinish()
                || this.courseRound.getCourseStatus().isHold()) {
            return false;
        }
        
        if(!this.scheduleStatus.isReady()) {
            return false;
        }
        
        Date today = new Date();
        
        String dateTime = DateFormatUtils.format(this.occurDate, "yyyy-MM-dd") + " " + this.occurHour + ":00";
        Date occurDateTime = DateUtils.parseDate(dateTime, "yyyy-MM-dd HH:mm:SS");
        
        long between = occurDateTime.getTime() - today.getTime();
        
        //3. decide could book or not
        if(this.course.isExperience()) {
            long days = between / (24 * 60 * 60 * 1000);
            return days < 1 ? false : true;
        }else {
        	return between > 0 ? true : false;
        }
    }
    
    /**
     * 
     * @return
     */
    @Transient
    public String signTimeString() {
        String dateTime = DateFormatUtils.format(this.occurDate, "yyyy-MM-dd") + " " + this.occurHour + ":00";
        Date occurDateTime = DateUtils.parseDate(dateTime, "yyyy-MM-dd HH:mm:SS");
        
        Date before = org.apache.commons.lang3.time.DateUtils.addHours(occurDateTime, -1);
        
        Date end = org.apache.commons.lang3.time.DateUtils.addHours(occurDateTime, 2);
        
        return DateFormatUtils.format(before, "HH:mm") + " 至 " + DateFormatUtils.format(end, "HH:mm");
    }
    
    /**
     * 
     * @return
     */
    private Date generateDate() {
        String dateTime = DateFormatUtils.format(this.occurDate, "yyyy-MM-dd") + " " + this.occurHour + ":00";
        Date occurDateTime = DateUtils.parseDate(dateTime, "yyyy-MM-dd HH:mm:SS");
        return occurDateTime;
    }
    
    /**
     * 
     * @param coach
     * @return
     */
    public CourseSchedule changeCoach(Principal coach) {
        this.coach = coach;
        return this;
    }
    
    /**
     * 
     * @param scheduleStatus
     * @return
     */
    public CourseSchedule changeStatus(ScheduleStatus scheduleStatus){
        this.scheduleStatus = scheduleStatus;
        return this;
    }
    
    /**
     * 
     * @return
     */
    public CourseSchedule changeToSchedule() {
        this.scheduleStatus = ScheduleStatus.SCHEDULE;
        this.site = null;
        this.coach = null;
        this.occurDate = null;
        this.occurHour = null;
        return this;
    }
    
    /**
     * 
     * @param day
     * @param hour
     * @return
     */
    public CourseSchedule arrange(String day, String hour) {
        
        // schedule day and hour
        this.occurDate = DateUtils.parseDate(day, "yyyy-MM-dd");
        this.occurHour = hour;
        
        // change status
        this.scheduleStatus = ScheduleStatus.SCHEDULE;
        
        return this;
    
    }
    
    /**
     * 
     * @param day
     * @param hour
     * @return
     */
    public CourseSchedule schedule(String day, String hour) {
        
        // schedule day and hour
        this.occurDate = DateUtils.parseDate(day, "yyyy-MM-dd");
        this.occurHour = hour;
        
        // change status
        this.scheduleStatus = ScheduleStatus.READY;
        
        return this;
    
    }
    
    /**
     * 
     * @param coach
     * @return
     */
    public CourseSchedule bindCoach(Principal coach) {
        
        // schedule day and hour
        this.coach = coach;
        
        // change status
        this.scheduleStatus = ScheduleStatus.READY;
        
        return this;
    
    }
    
    /**
     * 
     * @param day
     * @param hour
     * @return
     */
    public CourseSchedule editTime(Date day, String hour) {
        this.occurDate = day;
        this.occurHour = hour;
        return this;
    }
    
    /**
     * 
     * @param hour
     * @return
     */
    public CourseSchedule editHour(String hour) {
        this.occurHour = hour;
        return this;
    }
    
    /**
     * 
     * @param coach
     * @return
     */
    public CourseSchedule editCoach(Principal coach){
    	this.coach = coach;
    	return this;
    }
    
    /**
     * 
     * @param site
     * @return
     */
    public CourseSchedule editSite(Site site) {
        this.site = site;
        return this;
    }
    
    /**
     * 
     * @return
     */
    public CourseSchedule disabled() {
        this.disabled = true;
        return this;
    }
    
    /**
     * 
     * @param courseRound 
     * @return
     */
    public CourseSchedule addYearSchedule(CourseRound courseRound) {
        this.courseRound = courseRound;
        this.student = courseRound.getStudent();
        this.course = courseRound.getCourse();
        
        this.courseType = courseRound.getCourseType();
        this.scheduleStatus = ScheduleStatus.READY;
        
        this.disabled = false;
        return this;
    }
    
    /**
     * 
     * @return
     */
    public CourseSchedule addHalfHour(){
        String dayHour = com.demo2do.core.utils.StringUtils.connect(DateFormatUtils.format(this.occurDate, "yyyy-MM-dd"), " ", this.occurHour);
        Date dayHourTime = DateUtils.parseDate(dayHour, "yyyy-MM-dd HH:mm");
        Date result = org.apache.commons.lang3.time.DateUtils.addMinutes(dayHourTime, 30);
        String hour = DateFormatUtils.format(result, "HH:mm");
        CourseSchedule courseSchedule = new CourseSchedule(this.courseRound, this);
        courseSchedule.occurHour = hour;
        
        return courseSchedule;
    }
    
    /**
     * 
     * @return
     */
    public CourseSchedule cancel() {
        this.scheduleStatus = ScheduleStatus.SCHEDULE;
        this.coach = null;
        this.occurDate = null;
        this.occurHour = null;
        
        this.roomType = null;
        this.roomSchedule = null;
        return this;
    }
    
    /**
     * 
     * @return
     */
    public Date getSigninStartTime() {
        if(this.occurDate != null && !StringUtils.isEmpty(this.occurHour)) {
            String date = DateFormatUtils.format(this.occurDate, "yyyy-MM-dd") + " " + this.occurHour;
            Date startTime = DateUtils.parseDate(date, "yyyy-MM-dd HH:mm");
            return org.apache.commons.lang3.time.DateUtils.addHours(startTime, -1);
        } else {
            return null;
        }
    }
    
    /**
     * 
     * @return
     */
    public Date getSigninEndTime() {
        if(this.occurDate != null && !StringUtils.isEmpty(this.occurHour)) {
            String date = DateFormatUtils.format(this.occurDate, "yyyy-MM-dd") + " " + this.occurHour;
            Date endTime = DateUtils.parseDate(date, "yyyy-MM-dd HH:mm");
            return org.apache.commons.lang3.time.DateUtils.addHours(endTime, 2);
        } else {
            return null;
        }
    }
    
    /**
     * 
     * @return
     */
    @Transient
    public String getDayhour() {
        
        if(this.occurDate != null && StringUtils.isNotBlank(occurHour)) {
            return DateFormatUtils.format(this.occurDate) + "|" + this.occurHour; 
        }
        
        return null;
    }
    
    /**
     * @return
     */
    @Transient
    public String getDateTime() {
        
        if(this.occurDate != null && StringUtils.isNotBlank(this.occurHour)) {
            return DateFormatUtils.format(occurDate, "yyyy-MM-dd") + " " + occurHour + ":00";
        }
        
        return null;
        
    }
    
    /**
     * 
     * @return
     */
    @Transient
    public String getBeginData() {
        String date = DateFormatUtils.format(this.occurDate, "yyyyMMdd");
        String hour = StringUtils.replace(this.occurHour, ":", "");
        return this.coach.getId().toString() + date + hour;
    }
    
    /**
     * 
     * @return
     */
    @Transient
    public String getEndData() {
        String date = DateFormatUtils.format(this.occurDate, "yyyyMMdd");
        Date currentDate = DateUtils.parseDate(DateFormatUtils.format(this.occurDate, "yyyy-MM-dd ") + this.occurHour, "yyyy-MM-dd HH:mm");
        Date endDate = null;
        if(this.course.isExperience()) {
            endDate = org.apache.commons.lang3.time.DateUtils.addMinutes(currentDate, 90);
        } else {
            endDate = org.apache.commons.lang3.time.DateUtils.addHours(currentDate, 1);
        }
        String hour = DateFormatUtils.format(endDate, "HHmm");
        return this.coach.getId().toString() + date + hour;
    }

    /**
     * 
     * @param grade
     * @return
     */
    public CourseSchedule grade(int grade) {
        this.grade = grade;
        return this;
    }

    /**
     * 
     * @param content
     * @return
     */
    public CourseSchedule editContent(String content) {
        this.content = content;
        return this;
    }

    /**
     * 
     * @param contents
     * @return
     */
    public CourseSchedule editContent(List<ExerciseContent> contents) {
        if(contents != null && !contents.isEmpty()) {
            StringBuffer buffer = new StringBuffer();
            
            for(ExerciseContent content : contents) {
                buffer.append(content.ordinal() + ",");
            }
            
            this.content = buffer.toString();
        } else {
            this.content = null;
        }
        
        return this;
    }

    /**
     * 
     * @return
     */
    @Transient
    public List<ExerciseContent> getContentList() {
        List<ExerciseContent> contents = new ArrayList<>();
        
        if(this.content != null) {
            String[] temps = content.split(",");
            
            if(temps != null && temps.length > 0) {
                for(String temp : temps) {
                    contents.add(ExerciseContent.ordinalOf(Integer.parseInt(temp)));
                }
            }
        }
        
        return contents;
    }

    /**
     * 门店名称
     * @return
     */
    public String getSiteName() {
        return site == null ? null : site.getName();
    }

    /**
     * 教练名称
     * @return
     */
    public String getCoachName() {
        return coach == null ? null : coach.getName();
    }

    /**
     * 
     * @return
     */
    public String getStudentAvatar() {
        return this.student.getUser().getProfile() == null ? null : this.student.getUser().getProfile().getAvatar();
    }

    /**
     * 
     * @return
     */
    public String getStudentName() {
        return this.student.getName();
    }

    /**
     * 
     * @return
     */
    public String getStudentMobile() {
        return this.student.getMobile();
    }

    /**
     * 
     * @return
     */
    public String getCourseName() {
        return this.course.getName();
    }

    /**
     * 
     * @return
     */
    public String getContentString() {
        StringBuffer buffer = new StringBuffer();
        
        if(this.content != null) {
            String[] temps = content.split(",");
            
            if(temps != null && temps.length > 0) {
                for(String temp : temps) {
                    ExerciseContent exerciseContent = ExerciseContent.ordinalOf(Integer.parseInt(temp));
                    
                    if(exerciseContent != null) {
                        buffer.append(exerciseContent.getDescription() + "+");
                    }
                }
            }
        }
        
        if(buffer.length() == 0) {
            return null;
        } else {
            return buffer.substring(0, buffer.length() - 1);
        }
    }
    /**
     * @return the id
     */
    public Long getId() {
        return id;
    }

    /**
     * @return the sequence
     */
    public int getSequence() {
        return sequence;
    }

    /**
     * @return the student
     */
    public Student getStudent() {
        return student;
    }

    /**
     * @return the coach
     */
    public Principal getCoach() {
        return coach;
    }

    /**
     * @return the course
     */
    public Course getCourse() {
        return course;
    }

    /**
     * @return the site
     */
    public Site getSite() {
        return site;
    }

    /**
     * @return the courseRound
     */
    public CourseRound getCourseRound() {
        return courseRound;
    }

    /**
     * @return the courseType
     */
    public CourseType getCourseType() {
        return courseType;
    }

    /**
     * @return the occurDate
     */
    public Date getOccurDate() {
        return occurDate;
    }

    /**
     * @return the occurHour
     */
    public String getOccurHour() {
        return occurHour;
    }

    /**
     * @return the roomSchedule
     */
    public RoomScheduleRecord getRoomSchedule() {
        return roomSchedule;
    }

    /**
     * @return the roomType
     */
    public RoomType getRoomType() {
        return roomType;
    }

    /**
     * @return the scheduleStatus
     */
    public ScheduleStatus getScheduleStatus() {
        return scheduleStatus;
    }

    /**
     * @return the comment
     */
    public String getComment() {
        return comment;
    }

    /**
     * @return the grade
     */
    public Integer getGrade() {
        return grade;
    }

    /**
     * @return the content
     */
    public String getContent() {
        return content;
    }

    /**
     * @return the signinTime
     */
    public Date getSigninTime() {
        return signinTime;
    }

    /**
     * @return the disabled
     */
    public boolean isDisabled() {
        return disabled;
    }

    /**
     * @param id the id to set
     */
    public void setId(Long id) {
        this.id = id;
    }

    /**
     * @param sequence the sequence to set
     */
    public void setSequence(int sequence) {
        this.sequence = sequence;
    }

    /**
     * @param student the student to set
     */
    public void setStudent(Student student) {
        this.student = student;
    }

    /**
     * @param coach the coach to set
     */
    public void setCoach(Principal coach) {
        this.coach = coach;
    }

    /**
     * @param course the course to set
     */
    public void setCourse(Course course) {
        this.course = course;
    }

    /**
     * @param site the site to set
     */
    public void setSite(Site site) {
        this.site = site;
    }

    /**
     * @param courseRound the courseRound to set
     */
    public void setCourseRound(CourseRound courseRound) {
        this.courseRound = courseRound;
    }

    /**
     * @param courseType the courseType to set
     */
    public void setCourseType(CourseType courseType) {
        this.courseType = courseType;
    }

    /**
     * @param occurDate the occurDate to set
     */
    public void setOccurDate(Date occurDate) {
        this.occurDate = occurDate;
    }

    /**
     * @param occurHour the occurHour to set
     */
    public void setOccurHour(String occurHour) {
        this.occurHour = occurHour;
    }

    /**
     * @param roomSchedule the roomSchedule to set
     */
    public void setRoomSchedule(RoomScheduleRecord roomSchedule) {
        this.roomSchedule = roomSchedule;
    }

    /**
     * @param roomType the roomType to set
     */
    public void setRoomType(RoomType roomType) {
        this.roomType = roomType;
    }

    /**
     * @param scheduleStatus the scheduleStatus to set
     */
    public void setScheduleStatus(ScheduleStatus scheduleStatus) {
        this.scheduleStatus = scheduleStatus;
    }

    /**
     * @param comment the comment to set
     */
    public void setComment(String comment) {
        this.comment = comment;
    }

    /**
     * @param grade the grade to set
     */
    public void setGrade(Integer grade) {
        this.grade = grade;
    }

    /**
     * @param content the content to set
     */
    public void setContent(String content) {
        this.content = content;
    }

    /**
     * @param signinTime the signinTime to set
     */
    public void setSigninTime(Date signinTime) {
        this.signinTime = signinTime;
    }

    /**
     * @param disabled the disabled to set
     */
    public void setDisabled(boolean disabled) {
        this.disabled = disabled;
    }
}
